// MyNFT.sol 功能验证测试脚本
// 使用方法: node function-tests.js <合约地址>

const { ethers } = require('ethers');
require('dotenv').config();

// 测试配置
const TEST_CONFIG = {
    rpcUrl: process.env.SEPOLIA_RPC_URL || "https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID",
    privateKey: process.env.PRIVATE_KEY,

    // 测试参数
    testMetadataURIs: [
        "ipfs://QmTestHash1",
        "ipfs://QmTestHash2",
        "ipfs://QmTestHash3",
        "ipfs://QmTestHash4",
        "ipfs://QmTestHash5"
    ],

    // 测试地址（可以是部署者地址或其他地址）
    testRecipient: null // 将在运行时设置
};

// 完整的合约ABI
const CONTRACT_ABI = [
    // 基础查询函数
    "function name() public view returns (string)",
    "function symbol() public view returns (string)",
    "function totalSupply() public view returns (uint256)",
    "function owner() public view returns (address)",
    "function baseURI() public view returns (string)",
    "function contractURI() public view returns (string)",
    "function nextTokenId() public view returns (uint256)",

    // 铸造函数
    "function mintNFT(address to, string memory _tokenURI) public payable returns (uint256)",
    "function batchMintNFT(address to, string[] memory tokenURIs) public payable returns (uint256[] memory)",

    // 查询函数
    "function tokenURI(uint256 tokenId) public view returns (string)",
    "function ownerOf(uint256 tokenId) public view returns (address)",
    "function balanceOf(address owner) public view returns (uint256)",
    "function tokensOfOwner(address owner) public view returns (uint256[])",
    "function exists(uint256 tokenId) public view returns (bool)",

    // 配置查询
    "function maxSupply() public view returns (uint256)",
    "function maxMintPerAddress() public view returns (uint256)",
    "function mintedCount(address) public view returns (uint256)",
    "function mintPrice() public view returns (uint256)",
    "function publicMintEnabled() public view returns (bool)",

    // 管理员函数
    "function setTokenURI(uint256 tokenId, string memory _tokenURI) public",
    "function setBaseURI(string memory newBaseURI) public",
    "function setContractURI(string memory contractURI_) public",
    "function updateConfig(uint256 _maxSupply, uint256 _maxMintPerAddress, uint256 _mintPrice, bool _publicMintEnabled) public",
    "function withdraw() public",

    // 事件
    "event NFTMinted(address indexed to, uint256 indexed tokenId, string tokenURI)",
    "event MetadataUpdate(uint256 indexed tokenId, string newURI)",
    "event ConfigUpdate(uint256 maxSupply, uint256 maxMintPerAddress, uint256 mintPrice, bool publicMintEnabled)",
    "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
];

class NFTTester {
    constructor(contractAddress) {
        this.contractAddress = contractAddress;
        this.provider = new ethers.providers.JsonRpcProvider(TEST_CONFIG.rpcUrl);
        this.wallet = new ethers.Wallet(TEST_CONFIG.privateKey, this.provider);
        this.contract = new ethers.Contract(contractAddress, CONTRACT_ABI, this.wallet);
        this.testResults = [];
    }

    async runAllTests() {
        console.log("🧪 开始MyNFT合约功能验证测试...\n");
        console.log("📋 合约地址:", this.contractAddress);
        console.log("👤 测试地址:", this.wallet.address);
        console.log("🌐 网络:", (await this.provider.getNetwork()).name, "\n");

        // 设置测试接收者
        TEST_CONFIG.testRecipient = this.wallet.address;

        try {
            await this.testBasicInfo();
            await this.testMinting();
            await this.testQuerying();
            await this.testLimits();
            await this.testAdminFunctions();
            await this.testBatchMinting();
            await this.testEdgeCases();

            this.printTestSummary();

        } catch (error) {
            console.error("❌ 测试过程中发生错误:", error.message);
        }
    }

    async testBasicInfo() {
        console.log("1️⃣ 基础信息测试");
        console.log("=" * 50);

        try {
            const name = await this.contract.name();
            const symbol = await this.contract.symbol();
            const totalSupply = await this.contract.totalSupply();
            const owner = await this.contract.owner();
            const baseURI = await this.contract.baseURI();
            const contractURI = await this.contract.contractURI();
            const nextTokenId = await this.contract.nextTokenId();

            console.log("✅ 合约名称:", name);
            console.log("✅ 合约符号:", symbol);
            console.log("✅ 总供应量:", totalSupply.toString());
            console.log("✅ 合约所有者:", owner);
            console.log("✅ 基础URI:", baseURI);
            console.log("✅ 合约URI:", contractURI || "未设置");
            console.log("✅ 下一个Token ID:", nextTokenId.toString());

            this.testResults.push({ test: "基础信息", status: "PASS" });

        } catch (error) {
            console.log("❌ 基础信息测试失败:", error.message);
            this.testResults.push({ test: "基础信息", status: "FAIL", error: error.message });
        }

        console.log();
    }

    async testMinting() {
        console.log("2️⃣ 铸造功能测试");
        console.log("=" * 50);

        try {
            // 测试单个NFT铸造
            console.log("🎨 测试单个NFT铸造...");
            const testMetadataURI = TEST_CONFIG.testMetadataURIs[0];

            const tx = await this.contract.mintNFT(TEST_CONFIG.testRecipient, testMetadataURI);
            const receipt = await tx.wait();

            const mintEvent = receipt.events?.find(e => e.event === 'NFTMinted');
            const tokenId = mintEvent?.args?.tokenId;

            console.log("✅ 铸造成功");
            console.log("   - Token ID:", tokenId.toString());
            console.log("   - 接收者:", mintEvent?.args?.to);
            console.log("   - 元数据URI:", mintEvent?.args?.tokenURI);
            console.log("   - 交易哈希:", tx.hash);

            this.testResults.push({ test: "单个铸造", status: "PASS", tokenId: tokenId.toString() });

        } catch (error) {
            console.log("❌ 单个铸造测试失败:", error.message);
            this.testResults.push({ test: "单个铸造", status: "FAIL", error: error.message });
        }

        console.log();
    }

    async testQuerying() {
        console.log("3️⃣ 查询功能测试");
        console.log("=" * 50);

        try {
            // 获取第一个Token ID进行测试
            const totalSupply = await this.contract.totalSupply();
            if (totalSupply.gt(0)) {
                const tokenId = 1; // 第一个Token ID

                // 测试所有权查询
                const owner = await this.contract.ownerOf(tokenId);
                console.log("✅ Token", tokenId, "的所有者:", owner);

                // 测试余额查询
                const balance = await this.contract.balanceOf(owner);
                console.log("✅ 地址", owner, "的余额:", balance.toString());

                // 测试元数据查询
                const tokenURI = await this.contract.tokenURI(tokenId);
                console.log("✅ Token", tokenId, "的URI:", tokenURI);

                // 测试存在性查询
                const exists = await this.contract.exists(tokenId);
                console.log("✅ Token", tokenId, "是否存在:", exists);

                // 测试拥有的Token列表
                const tokenIds = await this.contract.tokensOfOwner(owner);
                console.log("✅ 地址拥有的Token IDs:", tokenIds.map(id => id.toString()));

                this.testResults.push({ test: "查询功能", status: "PASS" });
            } else {
                console.log("⚠️  没有Token可供查询测试");
                this.testResults.push({ test: "查询功能", status: "SKIP" });
            }

        } catch (error) {
            console.log("❌ 查询功能测试失败:", error.message);
            this.testResults.push({ test: "查询功能", status: "FAIL", error: error.message });
        }

        console.log();
    }

    async testLimits() {
        console.log("4️⃣ 限制功能测试");
        console.log("=" * 50);

        try {
            const maxSupply = await this.contract.maxSupply();
            const maxMintPerAddress = await this.contract.maxMintPerAddress();
            const mintedCount = await this.contract.mintedCount(TEST_CONFIG.testRecipient);
            const mintPrice = await this.contract.mintPrice();
            const publicMintEnabled = await this.contract.publicMintEnabled();

            console.log("✅ 最大供应量:", maxSupply.toString());
            console.log("✅ 每地址最大铸造:", maxMintPerAddress.toString());
            console.log("✅ 已铸造数量:", mintedCount.toString());
            console.log("✅ 铸造价格:", ethers.utils.formatEther(mintPrice), "ETH");
            console.log("✅ 公开铸造是否开启:", publicMintEnabled);

            this.testResults.push({ test: "限制功能", status: "PASS" });

        } catch (error) {
            console.log("❌ 限制功能测试失败:", error.message);
            this.testResults.push({ test: "限制功能", status: "FAIL", error: error.message });
        }

        console.log();
    }

    async testAdminFunctions() {
        console.log("5️⃣ 管理员功能测试");
        console.log("=" * 50);

        try {
            const owner = await this.contract.owner();
            const isOwner = owner.toLowerCase() === this.wallet.address.toLowerCase();

            if (isOwner) {
                console.log("✅ 当前地址是合约所有者，可以测试管理员功能");

                // 测试设置基础URI
                const newBaseURI = "https://new-base-uri.com/";
                const tx1 = await this.contract.setBaseURI(newBaseURI);
                await tx1.wait();
                console.log("✅ 基础URI设置成功");

                // 验证设置结果
                const updatedBaseURI = await this.contract.baseURI();
                console.log("✅ 更新后的基础URI:", updatedBaseURI);

                // 测试更新配置
                const tx2 = await this.contract.updateConfig(2000, 20, ethers.utils.parseEther("0.01"), true);
                await tx2.wait();
                console.log("✅ 配置更新成功");

                this.testResults.push({ test: "管理员功能", status: "PASS" });

            } else {
                console.log("⚠️  当前地址不是合约所有者，跳过管理员功能测试");
                this.testResults.push({ test: "管理员功能", status: "SKIP" });
            }

        } catch (error) {
            console.log("❌ 管理员功能测试失败:", error.message);
            this.testResults.push({ test: "管理员功能", status: "FAIL", error: error.message });
        }

        console.log();
    }

    async testBatchMinting() {
        console.log("6️⃣ 批量铸造测试");
        console.log("=" * 50);

        try {
            const batchURIs = TEST_CONFIG.testMetadataURIs.slice(0, 3); // 测试3个NFT

            console.log("🎨 测试批量铸造", batchURIs.length, "个NFT...");

            const tx = await this.contract.batchMintNFT(TEST_CONFIG.testRecipient, batchURIs);
            const receipt = await tx.wait();

            const mintEvents = receipt.events?.filter(e => e.event === 'NFTMinted');
            const tokenIds = mintEvents?.map(e => e.args?.tokenId.toString());

            console.log("✅ 批量铸造成功");
            console.log("   - 铸造数量:", mintEvents?.length || 0);
            console.log("   - Token IDs:", tokenIds?.join(', '));
            console.log("   - 交易哈希:", tx.hash);

            this.testResults.push({ test: "批量铸造", status: "PASS", count: mintEvents?.length || 0 });

        } catch (error) {
            console.log("❌ 批量铸造测试失败:", error.message);
            this.testResults.push({ test: "批量铸造", status: "FAIL", error: error.message });
        }

        console.log();
    }

    async testEdgeCases() {
        console.log("7️⃣ 边界情况测试");
        console.log("=" * 50);

        try {
            // 测试查询不存在的Token
            const nonExistentToken = 99999;
            try {
                await this.contract.ownerOf(nonExistentToken);
                console.log("❌ 应该抛出错误但没有");
            } catch (error) {
                console.log("✅ 查询不存在的Token正确抛出错误");
            }

            // 测试铸造到零地址
            try {
                await this.contract.mintNFT(ethers.constants.AddressZero, "ipfs://test");
                console.log("❌ 应该抛出错误但没有");
            } catch (error) {
                console.log("✅ 铸造到零地址正确抛出错误");
            }

            this.testResults.push({ test: "边界情况", status: "PASS" });

        } catch (error) {
            console.log("❌ 边界情况测试失败:", error.message);
            this.testResults.push({ test: "边界情况", status: "FAIL", error: error.message });
        }

        console.log();
    }

    printTestSummary() {
        console.log("📊 测试结果总结");
        console.log("=" * 50);

        const passed = this.testResults.filter(r => r.status === "PASS").length;
        const failed = this.testResults.filter(r => r.status === "FAIL").length;
        const skipped = this.testResults.filter(r => r.status === "SKIP").length;
        const total = this.testResults.length;

        console.log(`✅ 通过: ${passed}`);
        console.log(`❌ 失败: ${failed}`);
        console.log(`⚠️  跳过: ${skipped}`);
        console.log(`📊 总计: ${total}`);

        if (failed > 0) {
            console.log("\n❌ 失败的测试:");
            this.testResults
                .filter(r => r.status === "FAIL")
                .forEach(r => console.log(`   - ${r.test}: ${r.error}`));
        }

        console.log("\n🎉 功能验证测试完成！");

        if (failed === 0) {
            console.log("🎊 所有测试通过！合约功能正常！");
        } else {
            console.log("⚠️  部分测试失败，请检查合约实现。");
        }
    }
}

// 主函数
async function main() {
    const contractAddress = process.argv[2];

    if (!contractAddress) {
        console.log("❌ 请提供合约地址");
        console.log("使用方法: node function-tests.js <合约地址>");
        process.exit(1);
    }

    if (!TEST_CONFIG.privateKey) {
        console.log("❌ 请设置PRIVATE_KEY环境变量");
        process.exit(1);
    }

    const tester = new NFTTester(contractAddress);
    await tester.runAllTests();
}

// 运行测试
if (require.main === module) {
    main().catch(console.error);
}

module.exports = { NFTTester, TEST_CONFIG };
